home *** CD-ROM | disk | FTP | other *** search
- UNIT i2c_serial; {$project vt}
- { Steuert I²C-Bus Interface am seriellen Port des Amiga }
-
- INTERFACE;
-
- CONST maxerror=5;
-
- VAR i2c_error: ARRAY[0..maxerror] OF Str;
- VAR i2c_status, busdelay: Integer;
-
- PROCEDURE i2cbusIO(busaddr: byte; buffer: Ptr; data: Integer);
- PROCEDURE setregister(addr,reg,value: Byte);
- FUNCTION getregister(addr,reg: Byte): Byte;
- {$ulink "vt/s_i2cbusIO.o" }
-
- { ---------------------------------------------------------------------- }
-
- IMPLEMENTATION;
-
- {$opt q,s+,i+ } { keine Laufzeitprüfungen außer Stack und Feldindizes }
- {$incl "exec.lib", "intuition.lib", "hardware/cia.h" }
- {$incl "exec/semaphores.h", "resources/misc.h", "misc.lib" }
-
- CONST CLKHI = CIAF_COMRTS; CLKLO = NOT CLKHI; CLKIN = CIAF_COMCTS;
- DATAHI = CIAF_COMDTR; DATALO = NOT DATAHI; DATAIN = CIAF_COMCD;
- myname = 'I²C-bus';
- semname = 'i2c-serial';
- dummyname = 'i2c-sdummy';
-
- TYPE sem_mem = RECORD
- size: Word;
- sem: SignalSemaphore;
- name: String[15];
- END;
-
- VAR owner1,owner2: Ptr;
- ciab: ^CIA;
- mysem,dummysem: p_SignalSemaphore;
- mymem: ^sem_mem;
-
- PROCEDURE retreat;
- { Semaphore abbauen, ggf. Resourcen freigeben }
- BEGIN
- dummysem := FindSemaphore(dummyname);
- IF dummysem<>Nil THEN BEGIN { ich bin nicht allein }
- RemSemaphore(dummysem); { Dummy aus der Liste streichen }
- mymem := Ptr(Long(dummysem)-2);
- FreeMem(mymem,mymem^.size); { und Speicher freigeben }
- { Ich brauche keine Resources freizugeben! }
- END ELSE BEGIN { es läuft KEIN Partnerprogramm }
- mysem := FindSemaphore(semname);
- IF mysem<>Nil THEN BEGIN
- RemSemaphore(mysem);
- mymem := Ptr(Long(mysem)-2);
- FreeMem(mymem,mymem^.size);
- END;
- IF owner1 = Nil THEN FreeMiscResource(MR_SERIALBITS);
- IF owner2 = Nil THEN FreeMiscResource(MR_SERIALPORT);
- END;
- END;
-
- PROCEDURE user_meinung;
- { mit Alert nachfragen, ob ein fremder Ressourceninhaber ignoriert werden soll }
- VAR rache: Boolean;
- zeile1,zeile2: String[80];
- buf: String[200];
- xpos, l1, l2: Integer;
- BEGIN
- zeile1 := 'Serial ressources are owned by "';
- IF owner1<>Nil THEN zeile1 := zeile1 + copy(str(owner1),1,16);
- zeile1 := zeile1 + '"/"';
- IF owner2<>Nil THEN zeile1 := zeile1 + copy(str(owner2),1,16);
- zeile1 := zeile1 + '"!';
- l1 := length(zeile1);
- zeile2 := 'LEFT BUTTON = IGNORE '
- +' RIGHT BUTTON = OOPS ...';
- l2 := length(zeile2);
- buf := ' '+zeile1+' '+zeile2;
- xpos := 320 - 4*l1;
- buf[1] := chr(Hi(xpos)); buf[2] := chr(Lo(xpos));
- buf[3] := chr(16);
- buf[l1+4] := chr(0); buf [l1+5] := chr(1); { Fortsetzungsbyte }
- xpos := 320 - 4*l2;
- buf[l1+6] := chr(Hi(xpos)); buf[l1+7] := chr(Lo(xpos));
- buf[l1+8] := chr(32);
- buf [l1+l2+10] := chr(0); { Ende }
- OpenLib(IntuitionBase,'intuition.library',0);
- rache := DisplayAlert(RECOVERY_ALERT,buf,44);
- CloseLib(IntuitionBase);
- IF rache THEN BEGIN
- owner1 := Nil; owner2 := Nil;
- END ELSE
- Error('cannot allocate serial port!');
- END;
-
- PROCEDURE setup;
- { Semaphor einrichten, CIA-Register initialisieren, ggf. Ressourcen anfordern }
- BEGIN
- owner1 := Ptr(4); owner2 := Ptr(4); { Hauptsache <>Nil !!! }
- mymem := AllocMem(SizeOf(sem_mem),MEMF_PUBLIC);
- IF mymem=Nil THEN Error('no memory for semaphore');
- mymem^.size := SizeOf(sem_mem);
- dummysem := ^mymem^.sem;
- dummysem^.ss_Link.ln_Name := ^mymem^.name;
- dummysem^.ss_Link.ln_Type := NT_SEMAPHORE;
- mysem := FindSemaphore(semname); { bereits ein Semaphor installiert? }
- IF mysem=Nil THEN BEGIN { nein, ich bin allein }
- mymem^.name := semname;
- mysem := dummysem;
- AddSemaphore(mysem);
- { darum muß ich mich auch um die Resources kümmern }
- owner1 := ptr(AllocMiscResource(MR_SERIALBITS, myname));
- owner2 := ptr(AllocMiscResource(MR_SERIALPORT, myname));
- IF (owner1<>Nil) OR (owner2<>Nil) THEN
- user_meinung;
- END ELSE BEGIN { Partnerprogramm hat Semaphor schon installiert, }
- { ich muß aber noch einen Dummy-Semaphor aufstellen. }
- mymem^.name := dummyname;
- AddSemaphore(dummysem);
- owner1 := Nil; owner2 := Nil;
- END;
- ciab := ptr(Adr_ciab);
- { CTS- und DCD-Bit auf Eingang, RTS und DTR auf Ausgang }
- ciab^.ciaddra := (ciab^.ciaddra AND NOT (CIAF_COMCD OR CIAF_COMCTS))
- OR CIAF_COMRTS OR CIAF_COMDTR;
- END;
-
-
- { *** Ende der Init-/Cleanup-Routinen. Es folgen die Anwenderroutinen. }
-
-
- FUNCTION s_i2cbusIO(busaddr: byte; buffer: Ptr; data: Integer;
- busdelay: Integer): Integer; IMPORT;
-
- { Ich kann leider nicht direkt eine Assembler-Routine namens "i2cbusIO" }
- { importieren, da dann das Unit diesen Bezeichner sowohl importieren als }
- { auch exportieren müßte, hähä. }
-
- {$opt q,s+}
- PROCEDURE i2cbusIO{(busaddr: byte; buffer: Ptr; data: Integer)};
- { Startet den I²C-Bus und spricht den Chip mit Nr. <busaddr> an. Ist <data> }
- { positiv, werden <data> Bytes ab Adresse <buffer> über den Bus abgeschickt, }
- { sonst werden <-data> Bytes vom Bus geholt und ab Adresse <buffer> im }
- { Speicher abgelegt. Anschließend wird der I²C-Bus wieder gestoppt. }
- { Setzt als zusätzliche Rückmeldung die globale Variable "i2c_status": }
- { 0 = fehlerfreie Übertragung }
- { 1 = unquittierte Daten }
- { 2 = angesprochener Chip antwortet nicht }
- { 3 = gesendete Daten wurden zerstört }
- { 4 = gesendete Daten zu Null verfälscht }
- { 5 = gesendete Daten zu Einsen verfälscht }
- { Anmerkungen: }
- { 1. Das unterste Bit in <busaddr> wird ignoriert und entsprechend der }
- { I²C-Bus-Konvention auf 0 für Schreiben bzw. 1 für Lesen gesetzt. }
- { 2. Mehr Bytes zum Lesen anzufordern, als der bereitgestellte Puffer fassen }
- { kann, ist ein Fehler, der nicht erkannt wird und wahrscheinlich mit einem }
- { GURU endet. }
- { Die Variable <busdelay> steuert eine Zählschleife (sic!) und sollte auf }
- { normalen Amigas 0 sein. Für beschleunigte Amigas sollte hier ein geeigneter }
- { Wert den Bus auf die erlaubten 100 kHz bremsen können. }
- BEGIN
- ObtainSemaphore(mysem);
- i2c_status := s_i2cbusIO(busaddr,buffer,data,busdelay);
- ReleaseSemaphore(mysem);
- END;
- {VAR buf: ^Array[1..MAXINT] of byte;
- i,bit,send,recv,l: integer;
- x,y: byte;
- myCIAport: Byte ABSOLUTE $BFD000;
- LABEL panic;
- BEGIN
- ObtainSemaphore(mysem);
- i2c_status := 0
- buf := buffer;
- send := 0; recv := 0;
- IF data>0 THEN send := data else recv := -data;
- busaddr := busaddr AND $FE; IF recv>0 THEN busaddr := busaddr OR 1;
- { Bus starten: Protokollverletzung mit H->L }
- myCIAport := myCIAport OR CLKHI OR DATAHI; for l := 1 to busdelay DO;
- myCIAport := myCIAport AND DATALO; for l := 1 to busdelay DO;
- myCIAport := myCIAport AND CLKLO; for l := 1 to busdelay DO;
- { Daten senden, mindestens ein Byte für die Adressierung: }
- for i := 0 to send DO BEGIN
- IF i=0 THEN x := busaddr else x := buf^[i];
- y := 0; { sollte bei korrekter und ungestörter Hardware am Ende =x sein }
- for bit := 7 downto 0 DO BEGIN
- y := y SHL 1;
- IF ((x shr bit) AND $01) = 0 THEN
- myCIAport := myCIAport AND DATALO
- else
- myCIAport := myCIAport OR DATAHI;
- myCIAport := myCIAport OR CLKHI; for l := 1 to busdelay DO;
- IF (myCIAport AND DATAIN)<>0 THEN Inc(y);
- myCIAport := myCIAport AND CLKLO; for l := 1 to busdelay DO;
- END;
- IF y<>x THEN BEGIN
- IF y=$FF THEN i2c_status := 5
- ELSE IF y=0 THEN i2c_status := 4
- ELSE i2c_status := 3;
- GOTO panic;
- END;
- { Quittierungsbit lesen }
- myCIAport := myCIAport OR DATAHI;
- myCIAport := myCIAport OR CLKHI; for l := 1 to busdelay DO;
- IF (myCIAport AND DATAIN)<>0 THEN BEGIN
- { Quittierungsbit = H: bitte keine weiteren Daten, Abbruch. }
- { Falls das schon beim Senden der Adresse auftritt (i=0), hat überhaupt }
- { kein Busteilnehmer zugehört: falsche Adresse oder Hardwareproblem. }
- IF i=0 THEN i2c_status := 2 else i2c_status := 1;
- GOTO panic;
- END;
- myCIAport := myCIAport AND CLKLO; for l := 1 to busdelay DO;
- END;
- { Daten empfangen, sofern verlangt: }
- for i := 1 to recv DO BEGIN
- myCIAport := myCIAport OR DATAHI; { sonst liest man nur das eigene LO! }
- x := 0;
- for bit := 7 downto 0 DO BEGIN
- x := x shl 1;
- myCIAport := myCIAport OR CLKHI; for l := 1 to busdelay DO;
- IF (myCIAport AND DATAIN)<>0 THEN Inc(x);
- myCIAport := myCIAport AND CLKLO; for l := 1 to busdelay DO;
- END;
- { Quittierungsbit senden }
- IF i=recv THEN { letztes Byte mit HI quittieren, sonst LO }
- myCIAport := myCIAport OR DATAHI
- else
- myCIAport := myCIAport AND DATALO;
- myCIAport := myCIAport OR CLKHI; for l := 1 to busdelay DO;
- myCIAport := myCIAport AND CLKLO; for l := 1 to busdelay DO;
- buf^[i] := x;
- END;
- panic:
- { Bus stoppen: Protokollverletzung mit L->H }
- myCIAport := myCIAport AND CLKLO; for l := 1 to busdelay DO;
- myCIAport := myCIAport AND DATALO; for l := 1 to busdelay DO;
- myCIAport := myCIAport OR CLKHI; for l := 1 to busdelay DO;
- myCIAport := myCIAport OR DATAHI;
- ReleaseSemaphore(mysem);
- END;}
- {$opt i+}
-
- PROCEDURE setregister{(addr,reg,value: Byte)};
- { Häufig benötigter Vorgang: ein einzelnes Register am I²C-Bus beschreiben. }
- VAR bytes: array[1..2] of Byte;
- BEGIN
- bytes[1] := reg; bytes[2] := value;
- i2cbusIO(addr,^bytes,2);
- END;
-
- FUNCTION getregister{(addr,reg: byte): Byte};
- { Etwas umständlicher, wird dafür auch seltener benötigt: ein einzelnes }
- { Register auslesen. NICHT schleifenweise aufrufen, um mehrere Bytes zu }
- { lesen! Das läßt sich direkt über i2cbusIO() eleganter regeln! }
- VAR result: Byte;
- BEGIN
- i2cbusIO(addr,^reg,1);
- i2cbusIO(addr,^result,-1);
- getregister := result;
- END;
-
- BEGIN { Initialisierungsteil }
- busdelay := 0;
- i2c_error[0] := 'OK';
- i2c_error[1] := 'unquittierte Daten';
- i2c_error[2] := 'Chip antwortet nicht';
- i2c_error[3] := 'gesendete Daten zerstört';
- i2c_error[4] := 'SDA auf LO festgehalten';
- i2c_error[5] := 'SDA immer HI';
- MiscBase := OpenResource(MISCNAME);
- { Resource braucht *nicht* wieder geschlossen zu werden! }
- AddExitServer(retreat); setup;
- END.
-